#pragma once
#include <iostream>
#include <unistd.h>
#include <sys/epoll.h>

class Epoll
{
    const static int gnum = 256;      // epoll_create的参数（已废弃）
    const static int gtimeout = 5000; // 默认timeout的时间

public:
    Epoll(int timeout = gtimeout)
        : _epfd(-1),
          _timeout(timeout) // 这里的设置方式是在实例化Epoll类时传入，另外一种写法可以在调用epoll_wait的类成员函数接口中作为参数传入。（可根据自己的需求任意调整）
    {
    }


    ~Epoll()
    {
        if (_epfd >= 0)
            close(_epfd); // 析构需要释放掉epoll的fd
    }


    // 创建一个epoll实例：int epoll_create(int size);
    void CreateEpoll()
    {
        _epfd = epoll_create(gnum);
        if (_epfd < 0)
            exit(6); // On error, -1 is returned
    }


    // epoll对象的从就绪队列中捞取就绪事件：int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
    int WaitEpoll(struct epoll_event revs[], int revs_num)
    {
        return epoll_wait(_epfd, revs, revs_num, _timeout);
    }


    // 在下述分别实现：对epoll对象增删查改
    // int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

    // EPOLL_CTL_ADD：向 epoll 实例中添加一个新的文件描述符 fd 和相关的事件。
    bool AddFromEpoll(int sock, uint32_t events)
    {
        struct epoll_event ev;
        ev.events = events;
        ev.data.fd = sock;                                      // 这里，传入的sock是文件描述符
        return epoll_ctl(_epfd, EPOLL_CTL_ADD, sock, &ev) == 0; // When  successful, returns zero.
    }


    // EPOLL_CTL_DEL：从 epoll 实例中删除一个已存在的文件描述符 fd。
    bool DelFromEpoll(int sock)
    {
        return epoll_ctl(_epfd, EPOLL_CTL_DEL, sock, nullptr) == 0;
    }


    // EPOLL_CTL_MOD：修改已添加到 epoll 实例中的文件描述符 fd 的事件。
    bool ModFromEpoll(int sock, uint32_t events)
    {
        events |= EPOLLET; // 默认为LT模式，这里将epoll修改为ET模式（边缘触发）
        struct epoll_event ev;
        ev.events = events;
        ev.data.fd = sock;
        return epoll_ctl(_epfd, EPOLL_CTL_MOD, sock, &ev) == 0;
    }


private:
    int _epfd;
    int _timeout; // 指定多读转接的超时时间：这里，我们将其暴露给上层，可根据需要设置。
};